package com.hfi.security.app.server;

import com.hfi.security.core.properties.OAuth2ClientProperties;
import com.hfi.security.core.properties.SecurityProperties;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ChangLiang
 * @date 2019/8/30
 */
@Configuration
@EnableAuthorizationServer
public class HfiAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private WebResponseExceptionTranslator webResponseExceptionTranslator;

    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    /**
     * 在hfi.security.oauth2.storeType=redis的情况下 其是不生效的
     */
    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 自定义endpoint相关的信息
     * 如果你不extends AuthorizationServerConfigurerAdapter 那么系统会自己找到bean为authenticationManager及userDetailsService
     * 如果你extends AuthorizationServerConfigurerAdapter 那么就要自己指定这两个bean了
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
        endpoints.userDetailsService(userDetailsService);
        // 自定义tokenStore
        endpoints.tokenStore(tokenStore);
        if (jwtAccessTokenConverter != null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancers = new ArrayList<>();
            enhancers.add(jwtTokenEnhancer);
            /// 为什么要在这里加入jwtAccessTokenConverter 经过测试 这里不写也可以添加自定义内容 但是其令牌的默认方式
            // 加入jwtAccessTokenConverter 才会生成jwt令牌
            enhancers.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancers);
            endpoints
                    .tokenEnhancer(tokenEnhancerChain)
                    .accessTokenConverter(jwtAccessTokenConverter)
                    // 自定义处理器
                    .exceptionTranslator(webResponseExceptionTranslator);

        }
    }

    /**
     * 我们的授权服务器会给哪些第三方应用发放令牌
     * 指定了这个方法 我们在jpaDemo的application.properties中指定的appId和appSecret就不再起作用了
     * 因为我们这里只是允许我们自己的app来访问 而不是开放平台 所以使用inMemory模式
     * 如果是开放平台 那么要使用jdbc模式，指定数据源
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties client : securityProperties.getOauth2().getClients()) {
                builder
                        .withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        // 发出的令牌有效时间 2h
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                        // 支持的权限有哪些 第三方client发出的scope要在这里配置的数组内
                        // 可以理解为all read write
                        .scopes("all")
                        // 针对这个client支持的授权模式
                        .authorizedGrantTypes("refresh_token", "password", "authorization_code");
            }
        }
    }
}
